package com.utilities.parsers;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Element;

import com.utilities.dataStructures.Range;
import com.utilities.parsers.ContigParser.ParseAllContigsFileParser;
import com.utilities.parsers.ContigParser.ParseEvents;
import com.utilities.parsers.ContigParser.ParseListener;
import com.utilities.visualiser.PrepareXMLFile;

public class MapperContigStats extends ParseListener {
	
	private double contigCount = 0;
	private double totalContigLength = 0;
	private double numberOfReads = 0;
	private boolean makeScaffold = false;
	private String scaffoldOutFile = null;
	private String scaffoldInFile = null;
	private String gapListFile = null;
	private String contigListFile = null;
	private static String vXml = null;
	private FileWriter fw = null;
	private StringBuffer buffer = new StringBuffer();
	private int count;
	private int refStart = 1;
	private int totalGapLength = 0;
	private int seqLength = 66669;
	private ArrayList<Range> gapList = new ArrayList<Range>(); 
	private ArrayList<Range> contigList = new ArrayList<Range>();
	private ArrayList<Range> geneList = new ArrayList<Range>();
	private static PrepareXMLFile prepareXMLFile = null;
	private static String[] colors = {"maroon","red","blue","green","orange", "purple","fuchsia","navy",
		"lime","olive","yellow","teal","aqua","silver","gray","white","black"};
	public MapperContigStats(boolean makeScaffold, String scaffoldInFile, String scaffoldOutFile)
	{
		this.scaffoldOutFile = scaffoldOutFile;
		this.scaffoldInFile = scaffoldInFile;
		this.makeScaffold = makeScaffold;
	}
	
	public MapperContigStats(boolean makeScaffold, String scaffoldInFile, 
			String scaffoldOutFile, String gaList, String contigList, String vXml)
	{
		this.scaffoldOutFile = scaffoldOutFile;
		this.scaffoldInFile = scaffoldInFile;
		this.makeScaffold = makeScaffold;
		gapListFile = gaList;
		contigListFile = contigList;
		this.vXml = vXml;
	}
	
	public MapperContigStats setGeneList(ArrayList<Range> geneList)
	{
		this.geneList = geneList;
		return this;
	}
	public void main(String[] args) throws Exception
	{
//		printWriter = new PrintWriter(new FileWriter("C:/Users/bansalp/Desktop/tt.txt"));
		fw = new FileWriter(new File(scaffoldOutFile));
		ParseAllContigsFileParser allContigsFileParser = 
			new ParseAllContigsFileParser();
		allContigsFileParser.addListener(this);
		allContigsFileParser.parse(new FileReader(scaffoldInFile));
		System.out.println("Number of contigs : " + contigCount);
		System.out.println("Total Contig Length : " + totalContigLength);
		System.out.println("Average Contig Length : " + totalContigLength/contigCount);
		System.out.println("Total gap length : " + totalGapLength);
		System.out.println("Total number of gaps : " + gapList.size());
//		printWriter.flush();
//		printWriter.close();
		WriteToFile(gapList, gapListFile);
		WriteToFile(contigList, contigListFile);
		makeVisualisation(gapList,seqLength,contigList,geneList);
	}
	
	
	private static void makeVisualisation(ArrayList<Range> gaps,int seqLength, ArrayList<Range> contigs, ArrayList<Range> genes) throws ParserConfigurationException
	{
		String[] slots = new String[3];slots[0] = "Contigs"; slots[1] = "Gaps"; slots[2] = "Genes";
		initialise(String.valueOf(seqLength),slots);
		makeFeatureSlots(slots.length);
		
		for (Range range : contigs)
		{
			String mouseOver = "Start = " + String.valueOf(range.getLowerLimit())
			+ ", stop = " + String.valueOf(range.getUpperLimit());
			String name = range.getMetadata().replaceAll("\\b\\s{2,}\\b", " ").split(" ")[0].substring(1);
			Element feature = prepareXMLFile.createFeature("directStrand0", colors[0], "clockwise-arrow", 
					name, null,mouseOver,null,null,null);
			prepareXMLFile.createFeatureRange(feature, String.valueOf(range.getLowerLimit()), 
					String.valueOf(range.getUpperLimit()), null);
		}
		
		
		int i = 0;
		for (Range range : gaps)
		{
			String mouseOver = "Start = " + String.valueOf(range.getLowerLimit())
			+ ", stop = " + String.valueOf(range.getUpperLimit());
			Element feature = prepareXMLFile.createFeature("directStrand1", colors[1], "clockwise-arrow", 
			"gap_"+i, null,mouseOver,null,null,null);
			i++;
			prepareXMLFile.createFeatureRange(feature, String.valueOf(range.getLowerLimit()), 
					String.valueOf(range.getUpperLimit()), null);
		}
		int j = 0;
		for (Range range : genes)
		{
			String mouseOver = "Start = " + String.valueOf(range.getLowerLimit())
			+ ", stop = " + String.valueOf(range.getUpperLimit()) + 
			"length=" + String.valueOf(range.getUpperLimit() - range.getLowerLimit());
			Element feature = null;
			if (j%2 == 0)
			{
				feature = prepareXMLFile.createFeature("directStrand2", colors[2], "clockwise-arrow", 
						range.getMetadata(), null,mouseOver,null,null,null);
			}
			else
			{
				feature = prepareXMLFile.createFeature("directStrand3", colors[2], "clockwise-arrow", 
						range.getMetadata(), null,mouseOver,null,null,null);
			}
			j++;
			i++;
			prepareXMLFile.createFeatureRange(feature, String.valueOf(range.getLowerLimit()), 
					String.valueOf(range.getUpperLimit()), null);
		}
		prepareXMLFile.writeToFile(vXml);
	}
	
	private static void makeVisualisation(ArrayList<Range> gaps,int seqLength, ArrayList<Range> contigs) throws ParserConfigurationException
	{
		String[] slots = new String[2];slots[0] = "Contigs"; slots[1] = "Gaps";
		initialise(String.valueOf(seqLength),slots);
		makeFeatureSlots(slots.length);
		
		for (Range range : contigs)
		{
			String mouseOver = "Start = " + String.valueOf(range.getLowerLimit())
			+ ", stop = " + String.valueOf(range.getUpperLimit());
			String name = range.getMetadata().replaceAll("\\b\\s{2,}\\b", " ").split(" ")[0].substring(1);
			Element feature = prepareXMLFile.createFeature("directStrand0", colors[0], "clockwise-arrow", 
					name, null,mouseOver,null,null,null);
			prepareXMLFile.createFeatureRange(feature, String.valueOf(range.getLowerLimit()), 
					String.valueOf(range.getUpperLimit()), null);
		}
		
		
		int i = 0;
		for (Range range : gaps)
		{
			String mouseOver = "Start = " + String.valueOf(range.getLowerLimit())
			+ ", stop = " + String.valueOf(range.getUpperLimit());
			Element feature = prepareXMLFile.createFeature("directStrand1", colors[1], "clockwise-arrow", 
			"gap_"+i, null,mouseOver,null,null,null);
			i++;
			prepareXMLFile.createFeatureRange(feature, String.valueOf(range.getLowerLimit()), 
					String.valueOf(range.getUpperLimit()), null);
		}
		prepareXMLFile.writeToFile(vXml);
	}
	
	private static void initialise(String seqLength, String[] args) throws ParserConfigurationException 
	{
		prepareXMLFile = new PrepareXMLFile().createCGView(seqLength)
		.createLegend("sideLegend", "upper-right", "SanSerif, plain, 10", "0.8");
		
		for (int i = 0; i < args.length; i++)
		{
			String legendLabel = args[i];
			prepareXMLFile = prepareXMLFile.createLegendItem("sideLegend", legendLabel, "SanSerif, plain, 12", true, colors[i]);
		}
		prepareXMLFile = prepareXMLFile.createLegend("lowerleftlegend", "lower-left", "SanSerif, plain, 9", "0.8")
		.createLegendItem("lowerleftlegend", "This map was generated using CGView", null, false, null)
		.createLegend("lowerrightlegend", "lower-right", "SanSerif, plain, 9", "0.8")
		.createLegendItem("lowerrightlegend", "Work done at D-BSSE ETH Zurich", null, false, null);
	}
	
	private static void makeFeatureSlots(int slots) 
	{
		for(int i = 0; i < slots + 1; i++)
		{
			prepareXMLFile = prepareXMLFile.createFeatureSlots("directStrand" + i, "direct",false);
		}
//		prepareXMLFile = prepareXMLFile.createFeatureSlots("directStrand" + i, "direct",false);
	}
	private static void WriteToFile(ArrayList<Range> list, String fileName) throws IOException {
		PrintWriter pw = new PrintWriter(new FileWriter(fileName));
		for (Range range : list)
		{
			pw.println(range.getLowerLimit() + "\t" + range.getUpperLimit() + "\t" + range.getMetadata());
		}
		pw.flush();
		pw.close();
	}
	
	public double getContigCount() {
		return contigCount;
	}
	public double getTotalContigLength() {
		return totalContigLength;
	}
	public double getNumberOfReads() {
		return numberOfReads;
	}
	@Override
	public void handleEvent(ParseEvents event) {
		try
		{
			switch((ParseEvents.EventTypes)event.getEventType())
			{
			case docStarted:
				fw.write(">contig\n"); break;
				case contigStarted: 
				{
					this.contigCount++;
					String readLine = (String)event.getSource();
					String[] params = readLine.replaceAll("\\b\\s{2,}\\b", " ").split(" ");
					String contigName = params[0].trim().substring(1);
					int length = Integer.parseInt(params[3].trim().split("=")[1]);
					int refStart_curr = Integer.parseInt(params[2].split("\\.\\.")[0]);
					int refEnd_curr = Integer.parseInt(params[2].split("\\.\\.")[1]);
					int numberOfReads = Integer.parseInt(params[4].trim().split("=")[1]);
					if (refEnd_curr < refStart_curr)
					{
						int temp = refEnd_curr ; refEnd_curr = refStart_curr; refStart_curr = temp;
					}
					
					this.totalContigLength = this.totalContigLength + length;
					this.numberOfReads = this.numberOfReads + numberOfReads;
					
						if (refStart_curr > this.refStart)
						{
							writeToBuffer(null,refStart_curr - this.refStart + 1);
							this.gapList.add(new Range(this.refStart,refStart_curr - 1,contigName));
							this.totalGapLength = this.totalGapLength + refStart_curr - this.refStart + 1;
							this.contigList.add(new Range(refStart_curr,refEnd_curr,readLine));
						}
						this.refStart = refEnd_curr + 1;
					break;
				}
				case seqread :
				{
					if (makeScaffold)
					{
						String str = ((String)event.getSource()).trim();
						writeToBuffer(str,str.length());
						break;
					}
					
				}
				case docEnded : 
				{
					if (this.refStart < seqLength)
					{
						Range range = new Range(this.refStart, seqLength, "trailing");
						this.gapList.add(range);
						this.totalGapLength = this.totalGapLength + seqLength - this.refStart + 1;
					}
					if (makeScaffold)
					{
						write(); fw.close(); break;
					}
					break;
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	private void writeToBuffer(String str, int i) throws Exception 
	{
		if (str == null)
		{
			for (int j = 0 ; j < i ; j++)
			{
				buffer .append('N');
			}
		}
		else
		{
			buffer.append(str);
		}
		
		if (buffer.length() > 500)
		{
			write();
		}
	}
	
	private void write() throws Exception
	{
		for (int i = 0 ; i<buffer.length() ; i++)
		{
			fw.append(buffer.charAt(i));
			count++;
			if (count%60 == 0)
			{
				fw.append("\n");
			}
		}
		buffer = new StringBuffer();
		fw.flush();
	}
}
